6. Diálogo básico

6. Diálogo básico

Igual que los menús, se pueden construir durante la ejecución o a partir de un fichero de recursos

6.1. Ficheros de recursos

Cada IDE tiene un editor gráfico de diálogos, no obstante, es bueno entender la base que hay detrás.

Fichero decabecera:

// File:   ids.h

/* Identificadores */

/* Identificadores de comandos */
#define TEXTO             100
#define CM_DIALOGO        101
#define CM_DIALOGO_PARAM  102
// File:   ids.h

/* Identificadores */

/* Identificadores de comandos */
#define TEXTO             100
#define CM_DIALOGO        101
#define CM_DIALOGO_PARAM  102

Fichero de recursos:

//File  win004.rc  
#include <windows.h>
#include "ids.h"

BarraMenu MENU
BEGIN
 POPUP "&Principal"
 BEGIN
  MENUITEM "&Diálogo", CM_DIALOGO
  MENUITEM "&Con parámetro", CM_DIALOGO_PARAM
 END
END

DialogoPrueba DIALOG 0, 0, 118, 48 // id tipo posición y dimensiones
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION   // Dialog Styles DS_     Window Styles WS_
CAPTION "Diálogo de prueba" // Título del diálogo
FONT 8, "Helv"              // tipografía del diálogo
BEGIN
 CONTROL "Mensaje de prueba", TEXTO, "static", SS_LEFT | WS_CHILD | WS_VISIBLE, 8, 9, 84, 8
 CONTROL "Aceptar", IDOK, "button", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 56, 26, 50, 14
END
//File  win004.rc  
#include <windows.h>
#include "ids.h"

BarraMenu MENU
BEGIN
 POPUP "&Principal"
 BEGIN
  MENUITEM "&Diálogo", CM_DIALOGO
  MENUITEM "&Con parámetro", CM_DIALOGO_PARAM
 END
END

DialogoPrueba DIALOG 0, 0, 118, 48 // id tipo posición y dimensiones
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION   // Dialog Styles DS_     Window Styles WS_
CAPTION "Diálogo de prueba" // Título del diálogo
FONT 8, "Helv"              // tipografía del diálogo
BEGIN
 CONTROL "Mensaje de prueba", TEXTO, "static", SS_LEFT | WS_CHILD | WS_VISIBLE, 8, 9, 84, 8
 CONTROL "Aceptar", IDOK, "button", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 56, 26, 50, 14
END

Menú

Un menú “principal” con dos opciones “diálogo” y “con parámetro”.

El identificador “DialogoPruebas”, DIALOG, las coordenadas y dimensiones.

Estilos, las constantes para definir los estilos de ventana, que comienzan con WS_ (window style), puedes verlos con detalle en la sección de constantes “estilos de ventana”. Y los estilos de diálogos, que comienzan con DS_ (dialog style), en “estilos de diálogo”.

En la zona de controles hemos incluido un texto estático y un botón.

Etiqueta

El control static sirve para mostrar textos (etiquetas) o rectángulos (adornos)

CONTROL indica un elemento de diálogo.

A continuación introducimos el texto que mostrar.

Como ID del control ponemos -1 pues no se usa en la aplicación.

Después la clase de control “static” seguida por el estilo, las coordenadas X e Y, la anchura y la altura

Botón

Sirve para comunicar con el usuario con los mensajes de comandos como en los menús

CONTROL, el texto que mostrar, el ID que, se combina con el mensaje WM_COMMAND cuando el usuario hace clic en el botón.

La etiqueta IDOK está definida en el fichero Windows.h

La clase de control es “button” y luego el estilo

Un diálogo es una ventana, las ventanas tienen procedimiento de ventanas, los diálogos tienen procedimiento de diálogo que procesan los mensajes que les son enviados.

BOOL CALLBACK DialogProc(
    HWND hwndDlg,     // manipulador del diálogo
    UINT uMsg,             // mensaje
    WPARAM wParam, // 1er parámetro del mensaje
    LPARAM lParam     // 2o parámetro del mensaje
   );
BOOL CALLBACK DialogProc(
    HWND hwndDlg,     // manipulador del diálogo
    UINT uMsg,             // mensaje
    WPARAM wParam, // 1er parámetro del mensaje
    LPARAM lParam     // 2o parámetro del mensaje
   );

La diferencia con el procedimiento de ventana que ya hemos visto está en el tipo de valor de retorno, que en el caso del procedimiento de diálogo es de tipo booleano.

Excepto en la respuesta al mensaje WM_INITDIALOG, el procedimiento de diálogo debe retornar con un valor no nulo si procesa el mensaje y cero si no lo hace. Cuando responde a un mensaje WM_INITDIALOG, el procedimiento debe retornar cero si llama a la función SetFocus para poner el foco a uno de los controles del cuadro de diálogo. En otro caso, debe retornar un valor distinto de cero, y el sistema pondrá el foco en el primer control del diálogo que pueda recibirlo.

BOOL CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM);
BOOL CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM);

El mensaje WM_INITDIALOG lo usaremos para inicializar el diálogo antes de mostrarlo por pantalla. Debemos retornar un valor distinto de cero si no llamamos a SetFocus

El mensaje WM_COMMAND, procede del único botón del diálogo, cerraremos el diálogo llamando a la función EndDialog y retornaremos con un valor distinto de cero.

En cualquier otro caso retornamos FALSE, ya que no estaremos procesando el mensaje.

BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{ 
    switch (msg) /* manipulador del mensaje */ 
    { 
        case WM_INITDIALOG: 
            return TRUE; 
        case WM_COMMAND: 
            EndDialog(hDlg, FALSE); 
            return TRUE; 
    } 
    return FALSE;
}
BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{ 
    switch (msg) /* manipulador del mensaje */ 
    { 
        case WM_INITDIALOG: 
            return TRUE; 
        case WM_COMMAND: 
            EndDialog(hDlg, FALSE); 
            return TRUE; 
    } 
    return FALSE;
}

Lanzamos el diálogo desde el menú, el menú está en la pantalla así que la función de procesar pantalla WindowProcedure:

LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{ 
    static HINSTANCE hInstance; 
    static veces;

    switch (msg) /* manipulador del mensaje */
    { 
        case WM_CREATE: 
            hInstance = ((LPCREATESTRUCT)lParam)->hInstance; 
            return 0; 
        break; 
        case WM_COMMAND: 
            switch(LOWORD) 
            { 
                case CM_DIALOGO: 
                    DialogBox(hInstance, “DialogoPrueba”, hwnd, DlgProc); 
                break; 
                case CM_DIALOGO_PARAM: 
                    veces++; 
                    DialogBoxParam(hInstance, “DialogoPrueba”, hwnd, DlgProc2, veces); 
                break; 
            }
        break; 
        case WM_DESTROY: 
            PostQuitMessage(0); // envía un mensaje WM_QUIT a la cola de mensajes 
        break; 
        default: // para los mensajes de los que no nos ocupamos
            return DefWindowProc(hwnd, msg, wParam, lParam); 
    } 
    return 0; 
}
LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{ 
    static HINSTANCE hInstance; 
    static veces;

    switch (msg) /* manipulador del mensaje */
    { 
        case WM_CREATE: 
            hInstance = ((LPCREATESTRUCT)lParam)->hInstance; 
            return 0; 
        break; 
        case WM_COMMAND: 
            switch(LOWORD) 
            { 
                case CM_DIALOGO: 
                    DialogBox(hInstance, “DialogoPrueba”, hwnd, DlgProc); 
                break; 
                case CM_DIALOGO_PARAM: 
                    veces++; 
                    DialogBoxParam(hInstance, “DialogoPrueba”, hwnd, DlgProc2, veces); 
                break; 
            }
        break; 
        case WM_DESTROY: 
            PostQuitMessage(0); // envía un mensaje WM_QUIT a la cola de mensajes 
        break; 
        default: // para los mensajes de los que no nos ocupamos
            return DefWindowProc(hwnd, msg, wParam, lParam); 
    } 
    return 0; 
}

hInstance es una variable estática para tener siempre un manipulador a la instancia actual. Se le da valor en WM_CREATE que es un mensaje del inicio del programa. El mensaje WM_CREATE tiene como parámetro en lParam un puntero a una estructura CREATESTRUCT que contiene información sobre la ventana. En nuestro caso sólo nos interesa el campo hInstance.

Macro DialogBox

La otra novedad es la llamada a la función DialogBox, que crea el diálogo usando los parámetros:

Macro DialogBoxParam funciona igual que la otra macro pero permite enviar un parámetro más al procedimiento de diálogo. El parámetro es de tipo lParam con lo que puede ser entero o un puntero y tanto es de entrada como de salida.

En el ejemplo el diálogo con argumento lleva un contador estático y lo pasa a la función para que muestre el valor por pantalla.

static int veces;
(...)
case WM_COMMAND:
   switch(LOWORD(wParam)) 
   {
      case CM_DIALOGO:
         DialogBox(hInstance, "DialogoPrueba", hwnd, DlgProc);
      break;

      case CM_DIALOGO2:
         veces++;
         DialogBoxParam(hInstance, "DialogoPrueba2", hwnd, DlgProc2, veces);
      break;
   }
break;
(...)
static int veces;
(...)
case WM_COMMAND:
   switch(LOWORD(wParam)) 
   {
      case CM_DIALOGO:
         DialogBox(hInstance, "DialogoPrueba", hwnd, DlgProc);
      break;

      case CM_DIALOGO2:
         veces++;
         DialogBoxParam(hInstance, "DialogoPrueba2", hwnd, DlgProc2, veces);
      break;
   }
break;
(...)

y la función de proceso del segundo diálogo:

BOOL CALLBACK DlgProc2(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
    char texto[25];

    switch (msg)    /* manipulador del mensaje */
    {
        case WM_INITDIALOG:
           sprintf(texto, "Veces invocado: %d", (int)lParam);
           SetWindowText(GetDlgItem(hDlg, TEXTO), texto);
           return TRUE;

        case WM_COMMAND:
           EndDialog(hDlg, FALSE);
           return TRUE;
    }
    return FALSE;
}
BOOL CALLBACK DlgProc2(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
    char texto[25];

    switch (msg)    /* manipulador del mensaje */
    {
        case WM_INITDIALOG:
           sprintf(texto, "Veces invocado: %d", (int)lParam);
           SetWindowText(GetDlgItem(hDlg, TEXTO), texto);
           return TRUE;

        case WM_COMMAND:
           EndDialog(hDlg, FALSE);
           return TRUE;
    }
    return FALSE;
}

SetWindowText cambia el título de una ventana y también el texto de un control estático.

Es la forma más adecuada de comunicar variables entre la aplicación y los procedimientos de diálogo sin tener que recurrir a variables globales.